/* BeerScreen, Copyright (C) 2009  Gabriel Tremblay <initnull@gmail.com>
 * http://www.etrange.ca/beerscreen
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package ca.etrange.beerscreen.model;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.SAXException;

import ca.etrange.beerscreen.parser.BeerXMLParser;

// Data model for BSBeerWidget class.
// Represents a single beer.
// uses the beersmith beerxml parser.
public class Beer extends Observable implements Observer{
	// consts
	private static int KEG_CAPACITY_ML = 18900; // korny keg capacity in ml
	
	// class members
	private final int beerIndex;
	private boolean beerPresent = false;
	private boolean beerLevelMonitorPresent = false;
	
	// Data values from arduino readings
	private int beerKegLevelMl;				// used for beer level
	
	// Beer general infos
	private String beerName;
	private String beerNotes;
	private String beerTaste;

	// Helper classes
	private final SAXParserFactory saxFactory;
	private final BeerXMLParser beersmithParser;
	private final BeerFileMonitor fileMonitor;
	private SAXParser saxParser;
	
	// Data values from recipe parser
	// Correct code should use the declaration order as the display order
	public static enum BEER_SPECS {
		STYLE 			("Beer Style"), 	// BJCP Style
		ABV 			("Alc %"), 			// Alcohol percentage
		IBU 			("IBU's"), 			// BU
		YEAST 			("Yeast"), 			// beer yeast
		FERMENT_TEMP 	("Fermented at"), 	// Fermentation temperature in C
		BREW_DATE 		("Brewed on"), 		// Brewing date
		BOIL_TIME 		("Boiled for"), 	// Beer boil time
		OG 				("OG"), 			// Calculated OG
		FG 				("FG"), 			// Calculated FG
		MASH_TEMP 		("Mashed at"),		// Mash temperature
		BEER_NOTES		("Notes"),			// brewing notes
		BEER_TASTE		("Taste");			// taste
		
		private String displayValue;
		
		private BEER_SPECS(final String displayValue){
			this.displayValue = displayValue;
		}
		
		public String getDisplayValue(){
			return this.displayValue;
		}
	};
	
	// Specs set
	private final Map<BEER_SPECS, String> beerSpecs;

	/**
	 * Beer cstr
	 * @param beerIndex This is the index of the beer 1-4 in the current system
	 * the file loaded will be <index>-filename.xml such as 1-perlenoire.xml
	 * the index represents the tap number.
	 */
	public Beer(final int beerIndex){
		this.beerIndex = beerIndex;
		this.saxFactory = SAXParserFactory.newInstance();
		this.beersmithParser = new BeerXMLParser(this);
		this.beerSpecs = new LinkedHashMap<BEER_SPECS, String>();
		this.fileMonitor = new BeerFileMonitor(beerIndex, this);
	}
	
	/**
	 * Set a beer spec value
	 * @param beerSpec The spec
	 * @param specValue the value of the spec
	 */
	public void setBeerSpec(final BEER_SPECS beerSpec, final String specValue){
		beerSpecs.put(beerSpec, specValue);
	}
	
	/**
	 * @return all the current beer specs
	 */
	public Map<BEER_SPECS, String> getAllSpecs(){
		return beerSpecs;
	}
	
	/**
	 * Retreive a spec value. Will return empty string if value is not set
	 * @param beerSpec the beer spec.
	 * @return
	 */
	public String getBeerSpec(final BEER_SPECS beerSpec){
		String currentValue = beerSpecs.get(beerSpec);
		
		if(currentValue == null){
			currentValue = "";
		}
		return currentValue;
	}
	
	/**
	 * This is a callback method called by BsFileMonitor when the base
	 * file has changed. It updates the whole model and notify the views
	 * @param fileName
	 */
	public void updateBeerInfoCallback(String fileName){
		// Parse the xml
		try {
			this.saxParser = saxFactory.newSAXParser();
			this.saxParser.parse(fileName, this.beersmithParser);
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		// set status and inform the view
		this.setBeerPresent(true);
		this.setChanged();
		this.notifyObservers();
	}
	
	public void setBeerLevel(int level){
		beerKegLevelMl = level;
	}
	
	public int getBeerKegLevelPercentage(){
		float tmpPer = ((float)beerKegLevelMl / (float)KEG_CAPACITY_ML) * 100;
		tmpPer = Math.round(tmpPer);
		return (int)tmpPer;
	}
	
	public int getBeerKegLevelMl(){
		return beerKegLevelMl;
	}

	public boolean isBeerPresent() {
		return beerPresent;
	}

	public void setBeerPresent(boolean beerPresent) {
		this.beerPresent = beerPresent;
		
		// Beer has been disconnected, remove display (may not be needed)
		if(beerPresent == false){
			this.setChanged();
			this.notifyObservers();
		}
	}

	@Override
	public void update(Observable o, Object arg) {
		// beer level update
		if(o.getClass().equals(ArduinoBeerMonitor.class)){
			String values[] = (String[])arg;
			
			if(this.beerIndex-1 < values.length){
				// Value will be "k1:level", split at : and take second parameter
				int level = 
					Integer.parseInt((values[beerIndex-1].split(":"))[1]);
				// map to fit keg capacity
				// Readings are on 1024
				level = (int)((level / 1024.0) * KEG_CAPACITY_ML);
				
				this.setBeerLevel(level);
				this.setChanged();
				this.notifyObservers();
			}
		}
		
	}
	
	public String getBeerName() {
		return beerName;
	}

	public void setBeerName(String beerName) {
		this.beerName = beerName;
	}

	public String getBeerNotes() {
		return beerNotes;
	}

	public void setBeerNotes(String beerNotes) {
		this.beerNotes = beerNotes;
	}

	public String getBeerTaste() {
		return beerTaste;
	}

	public void setBeerTaste(String beerTaste) {
		this.beerTaste = beerTaste;
	}

	public boolean isBeerLevelMonitorPresent() {
		return beerLevelMonitorPresent;
	}

	public void setBeerLevelMonitorPresent(boolean beerLevelMonitorPresent) {
		this.beerLevelMonitorPresent = beerLevelMonitorPresent;
	}

	public int getBeerIndex() {
		return beerIndex;
	}
	
}
